home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pp / pp-6.0 / Chans / decnet / decnetsrvr.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-18  |  15.4 KB  |  666 lines

  1. /* decnectsrvr.c: DecNet Server */
  2.  
  3. # ifndef lint
  4. static char Rcsid[] = "@(#)$Header: /xtel/pp/pp-beta/Chans/decnet/RCS/decnetsrvr.c,v 6.0 1991/12/18 20:06:35 jpo Rel $";
  5. # endif
  6.  
  7. /*
  8.  * $Header: /xtel/pp/pp-beta/Chans/decnet/RCS/decnetsrvr.c,v 6.0 1991/12/18 20:06:35 jpo Rel $
  9.  *
  10.  * $Log: decnetsrvr.c,v $
  11.  * Revision 6.0  1991/12/18  20:06:35  jpo
  12.  * Release 6.0
  13.  *
  14.  */
  15.  
  16. /* decnet server designed to be started by dniserver (sunlink DNI)
  17.  
  18.    Unfortunately there seems to be no way of passing parameters
  19.    in to the slave process from dniserver, so we cannot pass in the
  20.    channel name. This is thus hardwired in as `decnet-in'.
  21.  
  22.    Sorry folks...
  23.  
  24.    Some design philosophy: we accept all recipient addresses, no matter
  25.    how preposterous. We then generate non-delivery reports. This means
  26.    that users of All-in-One and Mail Router actually get failure reports
  27.    in a timely fashion, rather than several days later which is what happens
  28.    if we reject the recipient addresses at the mail11 protocol level. Of course
  29.    it means that users of straight VMS mail have to wait a few extra seconds
  30.    for their failure notification, but what the hell, they shouldn't be
  31.    using it anyway.
  32.  
  33.    Mail11 To: and Cc: lines are accepted and mapped to X-Vms-To: and X-Vms-Cc:
  34.    in order to save us the possibly fruitless task of trying to parse and
  35.    convert what the incoming mailer dumps into these records. All recipients
  36.    are treated as primary recipients and inserted into a proper To: line
  37.  
  38.    We map the sender address into a From: line, after having been converted to
  39.    RFC822. Personal names are mapped into comments, as we cannot guarantee
  40.    the contents of the mail11 personal name data (and it is easier anyway).
  41.  
  42.    We add a Date: field. This of course is date of acceptance at the incoming
  43.    interface and bears no resemblance to actual date of posting.
  44.  
  45.  */
  46.  
  47. #include "head.h"
  48. #include "chan.h"
  49. #include "prm.h"
  50. #include "q.h"
  51. #include "ap.h"
  52. #include "retcode.h"
  53. #include "list_rchan.h"
  54.  
  55. #include <stdio.h>
  56. #include <sys/ioctl.h>
  57. #include <netdni/dni.h>
  58.  
  59. #define DEC_OK  0x01000000
  60. #define MAXLINE 78                              /* Max characters on header line */
  61. #define NET_TIMEOUT 120                 /* Timeout for net reads/writes */
  62.  
  63.  
  64. /* Externals */
  65. extern char *cont_822;
  66.  
  67. /* Internals */
  68. char *decnet_in = "decnet-in";                  /* Name of this channel */
  69. char *remote_host;                              /* name of the remote host */
  70. CHAN *mychan;
  71. int de_fd       = 3;                            /* The decnet file descriptor */
  72. int cc_ok       = 0;                            /* CC line OK */
  73. int map_space   = 0;                            /* Do we want to map spaces? */
  74. char *de_s2s();
  75. ADDR *to_first  = 0;                            /* Will contain list of recipients */
  76. ADDR *ad_sender;                                /* Will contain sender */
  77.  
  78.  
  79. /* ARGSUSED */
  80. main(argc, argv)
  81. int argc;
  82. char *argv[];
  83. {
  84.     int state = 0;
  85.     int addrs, mark;
  86.     RP_Buf rp;
  87.     char record[BUFSIZ];
  88.  
  89.     /* First initialise the channel */
  90.     chan_init(decnet_in);
  91.  
  92.     /* Do any initialisation */
  93.     chaninit();
  94.  
  95.     /* Now start the real work */
  96.     while (rp_isgood(de_get(record, sizeof(record), &mark)))
  97.     {
  98.         switch (state)
  99.         {
  100.         case 0:    /* Start of a message, getting sender */
  101.             do_sender(record);
  102.             addrs = 0;
  103.             state = 1;
  104.             break;
  105.         case 1:    /* Process addressees */
  106.             if (!mark)
  107.             {
  108.                 do_recip(record);        
  109.                 addrs++;
  110.             }
  111.             else
  112.             {
  113.                 if (rp_isbad(io_adend(&rp))
  114.                 || rp_isbad(io_tinit(&rp)) 
  115.                 || rp_isbad(io_tpart("hdr.822", 0, &rp)))
  116.                     error(rp.rp_line);
  117.                 state = 2;
  118.             }
  119.             break;
  120.         case 2:    /* Now get To: line */
  121.             do_to(record);
  122.             if (cc_ok)
  123.                 state = 3;
  124.             else
  125.                 state = 4;
  126.             break;
  127.         case 3:    /* Now get Cc: line if allowed */
  128.             do_cc(record);
  129.             state = 4;
  130.             break;
  131.         case 4:    /* Now get Subject: line */
  132.             do_subject(record);
  133.             if (rp_isbad(io_tdend(&rp))
  134.             || rp_isbad(io_tpart("2.ia5", 0, &rp)))
  135.                 error(rp.rp_line);
  136.             state = 5;
  137.             break;
  138.         case 5:    /* Now do the text */
  139.             if (!mark)
  140.                 do_txt(record);
  141.             else
  142.             {
  143.                 if (rp_isbad(io_tdend(&rp))
  144.                 || rp_isbad(io_tend(&rp)))
  145.                     error(rp.rp_line);
  146.                 do_status(addrs);
  147.                 state = 0;
  148.                 PP_NOTICE((">>> Message sent to %d recipients", addrs));
  149.             }
  150.             break;
  151.         }
  152.     }
  153.  
  154.     /* If here then connection has failed for some reason.
  155.        This may be normal */
  156.     if (state == 0)
  157.     {
  158.         PP_NOTICE(("Channel terminating normally"));
  159.         io_end(OK);
  160.     }
  161.     else
  162.     {
  163.         PP_NOTICE(("Channel terminated abnormally (state %d)", state));
  164.         io_end(NOTOK);
  165.     }
  166.     exit(0);
  167. }
  168.  
  169. /* This procedure does any initialisation. This includes accepting the
  170.    decnet connection */
  171.  
  172. chaninit()
  173. {
  174.     extern char *strstr();
  175.     SessionData sd;
  176.     OpenBlock ob;
  177.     struct prm_vars prm;
  178.     RP_Buf rp;
  179.  
  180.     PP_TRACE(("chaninit()"));
  181.  
  182.     /* Get access info */
  183.     if (ioctl(de_fd, SES_GET_AI, &ob) < 0)
  184.         error("failed to get access information");
  185.     remote_host = strdup(ob.op_node_name);
  186.  
  187.     /* Check channel OK */
  188.     if ((mychan = ch_mta2struct(decnet_in, remote_host)) == (CHAN *)0)
  189.         error("Channel not known");
  190.  
  191.     /* Do any initialising */
  192.     rename_log(mychan -> ch_name);
  193.     PP_NOTICE (("Connection from [%s] on channel %s", remote_host,
  194.             mychan -> ch_name));
  195.  
  196.     /* Accept the connection */
  197.     sd.sd_data.im_length = 0;
  198.     if (ioctl(de_fd, SES_ACCEPT, &sd) < 0)
  199.         error("decnet accept failed");
  200.  
  201.     /* Initialise IO system */
  202.     setuid(geteuid());
  203.     if (rp_isbad(io_init(&rp)))
  204.         error("failed to initialise incoming decnet channel");
  205.  
  206.     /* Set up the management parameters */
  207.     prm_init(&prm);
  208.     prm.prm_opts = PRM_ACCEPTALL;
  209.     if (rp_isbad(io_wprm(&prm, &rp)))
  210.         error("failed to set management parameters");
  211.     prm_free(&prm);
  212.  
  213.     /* Check config options */
  214.     map_space = (strstr(mychan->ch_in_info, "map_space") != NULLCP);
  215.  
  216.     PP_TRACE(("options: map_space(%d)", map_space));
  217.  
  218. }
  219.  
  220. /* This procedure takes a string containing the sender name in DEC format
  221.    and starts the message submission process
  222.  */
  223.  
  224. do_sender(sender)
  225. char *sender;
  226. {
  227.     char *cp;
  228.     Q_struct qp;
  229.     RP_Buf rp;
  230.     char lcl_sender[BUFSIZ];
  231.  
  232.     PP_TRACE(("do_sender(%s)", sender));
  233.  
  234.     /* Set up the per message parameters */
  235.     PP_TRACE(("set up message parameters"));
  236.     q_init(&qp);
  237.     /* qp.cont_type = strdup(cont_822); */
  238.     txt2listbpt(&qp.encodedinfo.eit_types, cp = strdup("ia5,hdr.822"));
  239.     free (cp);
  240.     qp.inbound = list_rchan_new(remote_host, mychan -> ch_name);
  241.     if (rp_isbad(io_wrq(&qp, &rp)))
  242.         error(rp.rp_line);
  243.     q_free(&qp);
  244.  
  245.     /* Set up the sender address */
  246.     PP_TRACE(("set up sender address"));
  247.     sprintf(lcl_sender, "%s::%s", remote_host, sender);
  248.     ad_sender = adr_new(de_s2s(lcl_sender), AD_ANY_TYPE, 0);
  249.     if (rp_isbad(io_wadr(ad_sender, AD_ORIGINATOR, &rp)))
  250.         error(rp.rp_line);
  251.  
  252. }
  253.  
  254. /* This procedure takes one recipient address, submits it, and returns
  255.    the DECnet status value. Note that we always accept addresses. If the
  256.    address is incorrect we return a non-delivery report. This is for the
  257.    benefit of the cretinous behaviour of VMS MRgate software which just
  258.    keeps on resubmitting failed mail at short intervals until it times out, 
  259.    several days later.
  260.  
  261.    A To: line is built up at the same time.
  262.  
  263.  */
  264.  
  265. do_recip(recip)
  266. char *recip;
  267. {
  268.     ADDR *ad_recip;
  269.     RP_Buf rp;
  270.  
  271.     PP_TRACE(("do_recip(%s)", recip));
  272.  
  273.     /* If we get a failure from io_wadr then this is fairly major,
  274.     as we have asked submit to handle non-delivery reports */
  275.  
  276.     ad_recip = adr_new(de_s2s(recip), AD_ANY_TYPE, 1);
  277.     if (rp_isbad(io_wadr(ad_recip, AD_RECIPIENT, &rp)))
  278.         error(rp.rp_line);
  279.     adr_add(&to_first, ad_recip);
  280.  
  281.     de_send_status(OK);
  282. }
  283.  
  284. /* This procedure takes a Cc: line and maps it to an X-Vms-Cc: line in the
  285.    header
  286.  */
  287.  
  288. do_cc(cc)
  289. char *cc;
  290. {
  291.     char line[BUFSIZ];
  292.  
  293.     PP_TRACE(("do_cc(%s)", cc));
  294.     sprintf(line, "X-Vms-Cc: %s\n", cc);
  295.     if (rp_isbad(io_tdata(line, strlen(line))))
  296.         error("failed to write Cc: line");
  297.     
  298. }
  299.  
  300. /* This procedure takes a To: line and maps it to a X-Vms-To: line in the
  301.    header. It also creates a new To: line out of the list of recipients
  302.    built by do_recip and a From: line out of ad_sender
  303.  */
  304.  
  305. do_to(to)
  306. char *to;
  307. {
  308.     char line[BUFSIZ];
  309.     ADDR *ad;
  310.  
  311.     PP_TRACE(("do_to(%s)", to));
  312.     sprintf(line, "X-Vms-To: %s\n", to);
  313.     if (rp_isbad(io_tdata(line, strlen(line))))
  314.         error("failed to write X-Vms-To: line");
  315.  
  316.     for (ad = to_first, strcpy(line, "To: "); ad; ad = ad->ad_next)
  317.     {
  318.         if ((strlen(line) + strlen(ad->ad_value) + 4) > MAXLINE)
  319.         {
  320.             strcat(line, "\n");
  321.             if (rp_isbad(io_tdata(line, strlen(line))))
  322.                 error("failed to write To: line");
  323.             strcpy(line, "    ");
  324.         }
  325.         strcat(line, ", ");
  326.         strcat(line, ad->ad_value);
  327.     }
  328.     strcat(line, "\n");
  329.     if (rp_isbad(io_tdata(line, strlen(line))))
  330.         error("failed to write To: line");
  331.     sprintf(line, "From: %s\n", ad_sender->ad_value);
  332.     PP_NOTICE(("From %s", ad_sender->ad_value));
  333.     if (rp_isbad(io_tdata(line, strlen(line))))
  334.         error("failed to write From: line");
  335.  
  336. }
  337.  
  338. /* This procedure takes a Subject: line and inserts it into the header
  339.  * also inserts Date: line
  340.  */
  341.  
  342. do_subject(subject)
  343. char *subject;
  344. {
  345.     char line[BUFSIZ], buf[BUFSIZ];
  346.     UTC     ut, lt;
  347.     extern UTC utclocalise();
  348.  
  349.     PP_TRACE(("do_subject(%s)", subject));
  350.     sprintf(line, "Subject: %s\n", subject);
  351.     if (rp_isbad(io_tdata(line, strlen(line))))
  352.         error("failed to write Subject: line");
  353.  
  354.     /* Do date */
  355.     ut = utcnow();
  356.     lt = utclocalise(ut);
  357.  
  358.     UTC2rfc(lt, buf);
  359.     sprintf(line, "Date: %s\n", buf);
  360.     if (rp_isbad(io_tdata(line, strlen(line))))
  361.         error("failed to write Date: line");
  362.     free ((char *)ut);
  363.     free ((char *)lt);
  364.  
  365. }
  366.  
  367. /* This procedure takes a single text record and inserts it into the body
  368.  */
  369.  
  370. do_txt(line)
  371. char *line;
  372. {
  373.     PP_TRACE(("do_txt(%s)", line));
  374.  
  375.     if ((strlen(line) && rp_isbad(io_tdata(line, strlen(line))))
  376.     || rp_isbad(io_tdata("\n", 1)))
  377.         error("failed to write text line");
  378.  
  379. }
  380.  
  381. /* This procedure sends `n' OK status messages back to DECnet */
  382.  
  383. do_status(n)
  384. int n;
  385. {
  386.  
  387.     PP_TRACE(("do_status(%d)", n));
  388.  
  389.     for ( ; n > 0 ; n--)
  390.         de_send_status(OK);
  391.  
  392. }
  393.  
  394. /* This procedure winds up in the event of error. It prints a notice in the
  395.    log, aborts the decnet connection (if still extant), aborts the message
  396.    submission process, and exits. It will be called in the event of any
  397.    failure in the protocol, as there is *no* recovery mechanism.
  398.  */
  399.  
  400. error(s)
  401. char *s;
  402. {
  403.     SessionData sd;
  404.  
  405.     PP_NOTICE(("error exit [%s]", s)); 
  406.  
  407.     /* Abort the DECnet link (don't bother to check return status) */
  408.     sd.sd_reason = 0;
  409.     sd.sd_data.im_length = 0;
  410.     ioctl(de_fd, SES_ABORT, &sd);
  411.  
  412.     /* Abort message submission process */
  413.     io_end(NOTOK);
  414.  
  415.     /* Farewell */
  416.     exit(1);
  417.  
  418. }
  419.  
  420. /* This procedure sends a status to the DECnet */
  421.  
  422. de_send_status(value)
  423. int value;
  424. {
  425.     long status;
  426.  
  427.     PP_TRACE(("de_send_status(%d)", value));
  428.  
  429.     status = DEC_OK;
  430.     if (timeout(NET_TIMEOUT))
  431.     {
  432.         error("decnet timeout in write");
  433.     }
  434.     if (write(de_fd, (char *)&status, sizeof(status)) != sizeof(status))
  435.         error("failed to write status value");
  436.     timeout(0);
  437.  
  438. }
  439.  
  440. /* This procedure takes an address in DECNet form and returns a parse tree.
  441.    Mail11 personal names are converted to comments. If the address
  442.    is surrounded by quotes then we assume it is not for conversion and feed
  443.    it into the normal address parser. We also force the address (not the
  444.    comments) into lower case, as I hate machines that SHOUT at me. Internal
  445.    (to the address) quotes are removed, as these seem to be generated by
  446.    MRGATE somewhat gratuitously.
  447.  */
  448.  
  449. AP_ptr de_s2t(s)
  450. char *s;
  451. {
  452.     AP_ptr  ap,
  453.         ap_start,
  454.         local_host = NULLAP,
  455.         route_host = NULLAP,
  456.         personal = NULLAP;
  457.     int state = 0;
  458.     char *t, *token, *token_end, *last_token;
  459.  
  460.     PP_TRACE(("de_s2t(%s)", s));
  461.     for (ap_start = ap = ap_alloc(), t = s; *t; t++)
  462.     {
  463.         switch (state)
  464.         {
  465.         case 0: /* Start of things. Looking for non_space */
  466.             if (*t == '"') /* Assume RFC822 address */
  467.             {
  468.                 token = t + 1;
  469.                 state = 5;
  470.             }
  471.             else
  472.             {
  473.                 *t = tolower(*t);
  474.                 token = t;
  475.                 state = 1;
  476.                 last_token = 0;
  477.             }
  478.             break;
  479.         case 1: /* Looking at host names or mailbox names */
  480.             if (*t == ':') /* Could be start of a :: */
  481.             {
  482.                 state = 2;
  483.             }
  484.             else if (*t == ' ') /* Could be mailbox */
  485.             {
  486.                 token_end = t;
  487.                 state = 3;
  488.             }
  489.             else
  490.             {
  491.                 *t = tolower(*t);
  492.             }
  493.             break;
  494.         case 2: /* Just got a :, looking for the next */
  495.             if (*t == ':') /* Bingo! Got a :: */
  496.             {
  497.                 *(t - 1) = '\0';
  498.  
  499.                 /* Now tack it into the parse tree */
  500.                 if (strstr(token, "mrgate") == 0 &&
  501.                     strstr(token, "a1") == 0 &&
  502.                     !( last_token && strstr(token, last_token)))
  503.                 {
  504.                     ap_fllnode(ap, AP_DOMAIN, token);
  505.                     route_host = local_host;
  506.                     local_host = ap;
  507.                     ap->ap_next = ap_alloc();
  508.                     ap->ap_ptrtype = AP_PTR_MORE;
  509.                     ap = ap->ap_next;
  510.                     last_token = token;
  511.                 }
  512.                 token = t + 1;
  513.                 state = 1;
  514.             }
  515.             else  /* Error */
  516.             {
  517.                 return (BADAP);
  518.             }
  519.             break;
  520.         case 3: /* May be end of user name */
  521.             if (*t == '"')  /* personal name */
  522.             {
  523.                 *token_end = '\0';
  524.                 ap_fllnode(ap, AP_MAILBOX, token);
  525.                 token = t + 1;
  526.                 state = 4;
  527.             }
  528.             else if (*t != ' ') /* Bother, not finished */
  529.             {
  530.                 *t = tolower(*t);
  531.                 state = 1;
  532.             }
  533.             break;
  534.         case 4: /* Collecting personal name */
  535.             break;
  536.         case 5: /* processing quoted address */
  537.             if (*t == '"')
  538.                 *t = '\0';
  539.             break;
  540.         }
  541.     }
  542.     /* Finish off if necessary */
  543.     if (state == 3)
  544.         *token_end = '\0';
  545.     if ((state == 1 || state == 3) &&
  546.         !(last_token && strstr(token, last_token)))
  547.         ap_fllnode(ap, AP_MAILBOX, token);
  548.     if (state == 4)
  549.     {
  550.         *(t - 1) = '\0';
  551.         personal = ap_new(AP_COMMENT, token);
  552.     }
  553.  
  554.     /* If state 5 then was processing RFC address */
  555.     if (state == 5)
  556.         ap_start = ap_s2t(token);
  557.     else /* We have to take last host and put after mailbox */
  558.     {
  559.         if (route_host)
  560.         {
  561.             ap_move(route_host, local_host);
  562.             if (personal)
  563.                 ap_insert(local_host, AP_PTR_MORE, personal);
  564.         }
  565.         else if (local_host)
  566.         {
  567.             ap->ap_next = local_host;
  568.             ap->ap_ptrtype = AP_PTR_MORE;
  569.             local_host->ap_next = NULLAP;
  570.             local_host->ap_ptrtype = AP_PTR_NIL;
  571.             ap_start = ap;
  572.             if (personal)
  573.                 ap_insert(local_host, AP_PTR_MORE, personal);
  574.         }
  575.         else if (personal)
  576.             ap_insert(ap, AP_PTR_MORE, personal);
  577.  
  578.     }
  579.  
  580.     /* Last of all we cruise down the tree removing spurious "s
  581.     in DOMAIN and MAILBOX tokens. Also at this point we map ' ' to '_'
  582.     if required */
  583.     for (ap = ap_start; ap; ap = ap->ap_next)
  584.     {
  585.         if (ap->ap_obtype == AP_DOMAIN || ap->ap_obtype == AP_MAILBOX)
  586.         {
  587.             char *s1, *d;
  588.  
  589.             for (s1 = d = ap->ap_obvalue; *s1; s1++)
  590.                 if (map_space && *s1 == ' ') *d++ = '_';
  591.                 else if (*s1 != '"') *d++ = *s1;
  592.             *d = '\0';
  593.         }
  594.     }
  595.  
  596.     return (ap_start);
  597. }
  598.  
  599. /* This procedure takes a decnet address and returns it mapped to RFC822
  600.  */
  601.  
  602. char *de_s2s(de)
  603. char *de;
  604. {
  605.     AP_ptr  tree;
  606.     char *rfc;
  607.  
  608.     PP_TRACE(("de_s2s(%s)", de));
  609.     ap_norm_all_domains();
  610.     if ((tree = de_s2t(de)) != BADAP)
  611.     {
  612.         ap_t2s(ap_normalize(tree, mychan->ch_ad_order), &rfc);
  613.         PP_TRACE(("returns: `%s'", rfc));
  614.         return (rfc);
  615.     }
  616.     PP_TRACE(("returns (fail): `%s'", de));
  617.     return (de);
  618. }
  619.  
  620. /* This procedure gets a single decnet record, null terminates it (stripping
  621.    off terminating carriage control if present). It sets mark if the mark
  622.    record was read.
  623.  */
  624.  
  625. de_get(s, n, m)
  626. char *s;
  627. int n, *m;
  628. {
  629.     int no_read;
  630.  
  631.     PP_TRACE(("de_get()"));
  632.  
  633.     /* Read a record */
  634.     if (timeout(NET_TIMEOUT))
  635.     {
  636.         PP_NOTICE(("read timed out"));
  637.         return RP_NIO;
  638.     }
  639.     if ((no_read = read(de_fd, s, n - 1)) < 0)
  640.     {
  641.         PP_DBG(("read failed [%d]", errno));
  642.         return RP_NIO;
  643.     }
  644.     timeout(0);
  645.  
  646.     /* Is it a mark? */
  647.     if (no_read == 1 && s[0] == '\0')
  648.     {
  649.         *m = TRUE;
  650.         PP_DBG(("returns `mark'"));
  651.         return RP_OK;
  652.     }
  653.  
  654.     /* terminate it, removing \r\n if present */
  655.     if (no_read >= 2 && s[no_read - 2] == '\r' && s[no_read - 1] == '\n')
  656.         s[no_read - 2] = '\0';
  657.     else
  658.         s[no_read] = '\0';
  659.  
  660.     /* clear mark and return */
  661.     *m = 0;
  662.     PP_DBG(("returns `%s'", s));
  663.     return RP_OK;
  664.  
  665. }
  666.